﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;

namespace Topro.Service
{
    using NLog;
    using Topro.Application;
    using Topro.Entity;
    using Topro.Entity.Query;
    using Topro.Entity.Response;
    using Topro.Entity.Table;
    using Topro.Enum;
    using Topro.Monitor;

    /// <summary>
    /// 堆垛机
    /// </summary>
    public class Stacker
    {
        #region 变量
        /// <summary>
        /// 日志委托
        /// </summary>
        public delegate void InfoDelegate(int type, Dictionary<string, string> dc);

        /// <summary>
        /// 日志事件
        /// </summary>
        public event InfoDelegate infoEvent;

        /// <summary>
        /// 站台消息委托
        /// </summary>
        public delegate void StationDelegate(int station, int status);

        /// <summary>
        /// 站台消息事件
        /// </summary>
        public event StationDelegate stationEvent;

        /// <summary>
        /// 剁机线程
        /// </summary>
        public Thread stackerThread = null;

        /// <summary>
        /// 读码器
        /// </summary>
        public List<BCRStation> bcrStations = null;

        /// <summary>
        /// 入库暂存站台
        /// </summary>
        public List<StockStation> inStockStations = null;

        /// <summary>
        /// 出库暂存站台
        /// </summary>
        public List<StockStation> outStockStations = null;

        /// <summary>
        /// 暂存站台
        /// </summary>
        public List<StockStation> stockStations = null;

        /// <summary>
        /// 入库队列
        /// </summary>
        Queue<int> inQueue = new Queue<int>();

        /// <summary>
        /// 出库队列
        /// </summary>
        Queue<int> outQueue = new Queue<int>();

        /// <summary>
        /// 剁机帮助类
        /// </summary>
        public PlcUtil stackerUtil = null;

        /// <summary>
        ///  输送线帮助类
        /// </summary>
        public PlcUtil transLineUtil = null;

        /// <summary>
        /// 
        /// </summary>
        public List<DeviceExceptionCodeResponse> deviceExceptionCode;

        /// <summary>
        /// 堆垛机
        /// </summary>
        public int stackerStationNumber;

        /// <summary>
        /// 输送线
        /// </summary>
        public int transLineStationNumber;

        /// <summary>
        /// 工作类型
        /// </summary>
        private OutInStatus outinStatus = OutInStatus.入库;

        /// <summary>
        /// 脉冲状态
        /// </summary>
        public int pulseStatus = 0;

        /// <summary>
        /// 日志
        /// </summary>
        private static readonly Logger logStacker = LogManager.GetLogger("Stacker");
        #endregion

        #region 主流程
        /// <summary>
        /// 开始堆垛机业务
        /// </summary>
        public void main()
        {
            while (true)
            {
                try
                {
                    Thread.Sleep(1000);
                    //序列号[D617]
                    var serialNo = stackerUtil.Get(StockAddr.SerialNo);
                    //是否空闲[D100]
                    var free = stackerUtil.Get(StockAddr.IsFree);
                    //工作中[D101]
                    var work = stackerUtil.Get(StockAddr.Work);
                    //异常[D120]
                    var exception = stackerUtil.Get(StockAddr.Exception);
                    //入库完成[D103]
                    var inDone = stackerUtil.Get(StockAddr.InDone);
                    //出库完成[D104]
                    var outDone = stackerUtil.Get(StockAddr.OutDone);

                    if (stockStations != null && stockStations.Any())
                    {
                        stockStations.ForEach(item =>
                        {
                            var exist = transLineUtil.Get(item.HaveTrayAddr);

                            if (exist == (int)WorkStatus.One)
                                stationEvent(item.Id, 1);
                            else
                                stationEvent(item.Id, 0);
                        });
                    }
                    //脉冲
                    if (pulseStatus == (int)WorkStatus.Zero)
                    {
                        pulseStatus = 1;
                        stackerUtil.Set(StockAddr.PulseAddr, (int)1);
                    }
                    else if (pulseStatus == (int)WorkStatus.One)
                    {
                        pulseStatus = 0;
                        stackerUtil.Set(StockAddr.PulseAddr, (int)0);
                    }
                    //入库完成
                    this.InDone();
                    //出库完成
                    this.OutDone();
                    //异常
                    if (exception == (int)Success.Two)
                    {
                        infoEvent(3, new Dictionary<string, string> { ["1"] = $"异常." });

                        for (int i = 2; i < 9; i++)
                        {
                            //异常地址
                            var addr = $"D12{i}";
                            //异常值
                            var addrValue = stackerUtil.Get(addr);
                            if (addrValue > 0)
                                GetDeviceException(deviceExceptionCode, addr, addrValue);
                        }

                        continue;
                    }
                    else
                    {

                        if (free == (int)Success.Two)
                        {
                            if (outinStatus == OutInStatus.入库)
                            {
                                infoEvent(1, new Dictionary<string, string> { ["1"] = $"入库." });
                                this.In();
                                outinStatus = OutInStatus.出库;
                            }
                            else if (outinStatus == OutInStatus.出库)
                            {
                                infoEvent(2, new Dictionary<string, string> { ["1"] = $"出库." });
                                this.Out();
                                outinStatus = OutInStatus.入库;
                            }
                        }
                        else if (work == (int)Success.Two)
                            infoEvent(4, new Dictionary<string, string> { ["1"] = $"工作中." });
                        else
                            infoEvent(5, new Dictionary<string, string> { ["1"] = $"半自动." });
                    }
                }
                catch (ThreadAbortException ex)
                {
                    infoEvent(90, new Dictionary<string, string> { ["1"] = $"{ex.Message}." });
                }
                catch (Exception ex)
                {
                    infoEvent(0, new Dictionary<string, string> { ["1"] = $"异常{ex.Message}." });
                }
            }
        }
        #endregion

        #region 出入库

        #region 入库
        /// <summary>
        /// 入库
        /// </summary>
        private void In()
        {
            try
            {
                #region get
                //托盘编码
                var stationIds = new List<int>();
                //获取有载流水号
                if (inStockStations != null && inStockStations.Any())
                {
                    inStockStations.ForEach(item =>
                    {
                        //站台有无载
                        var exits = this.transLineUtil.Get(item.HaveTrayAddr);
                        //是否有载
                        if (exits == (int)WorkStatus.One)
                        {
                            //stationIds.Add(string.Format("2-10-01-{0}", item.Id.ToString("00")));
                            var serialId = this.transLineUtil.Get(item.SerialNoAddr);
                            if (serialId > 0)
                                stationIds.Add(item.Id);
                            else
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({item.Name})无流水号]" });
                        }
                    });
                }

                if (!stationIds.Any())
                    stationIds.Add(0);

                var query = new ASRSJOBQuery()
                {
                    Type = new List<string>()
                    {
                        "In"
                    },
                    Where = string.Format("TYPE='In' And (ORIPORT LIKE '2-10-01-01' or ORIPORT LIKE '2-10-01-03' or ORIPORT LIKE '2-10-01-05' or ORIPORT LIKE '2-10-01-07' or ORIPORT LIKE '2-10-01-09' or ORIPORT LIKE '2-10-01-11' or ORIPORT LIKE '2-10-01-13' or ORIPORT LIKE '2-10-01-14')"),
                    State = (int)ASRSJOBStatus.待处理
                };
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetSingleASC(query);
                //指令对象是否为空
                if (asrsjob == null)
                    return;
                if (asrsjob.STATE == (int)WorkStatus.One)
                    return;
                #endregion

                #region from
                if (string.IsNullOrEmpty(asrsjob.ORIPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误(from):{asrsjob.ORIPORT}为空]" }); return;
                }

                var oriport = asrsjob.ORIPORT.Split('-');
                //验证长度
                if (oriport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误(from):{asrsjob.TARPORT}长度验证失败]" }); return;
                }
                //入库站台
                var fromStationId = int.Parse(oriport[3]);
                //入库站台对象
                var stockStation = stockStations.FirstOrDefault(c => c.Id == fromStationId);
                //站台验证
                if (stockStation == null)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({fromStationId})查找失败]" }); return;
                }
                //入库状态
                var fromHaveTrayStatus = transLineUtil.Get(stockStation.HaveTrayAddr);
                //入库流水号
                var fromSerialNo = transLineUtil.Get(stockStation.SerialNoAddr);
                //验证是否被占用
                if (fromHaveTrayStatus == (int)Success.Zero)
                    return;
                else
                {
                    if (fromSerialNo <= 0)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({stockStation.Name}) 未流水号.]" }); return;
                    }
                }

                var from = new From();
                //排
                from.Pai = stockStation.Pai;
                //列
                from.Lie = stockStation.Lie;
                //层
                from.Ceng = stockStation.Ceng;
                #endregion

                #region to
                //1-10-01-020305
                var tarport = asrsjob.TARPORT.Split('-');
                //验证长度
                if (tarport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误:{asrsjob.TARPORT}]" });
                }
                //分割
                var toArray = System.Text.RegularExpressions.Regex.Split(tarport[3], @"(?<=\G.{2})(?!$)");

                var to = new To();
                //排
                to.Pai = int.Parse(toArray[0]);
                //列
                to.Lie = int.Parse(toArray[1]);
                //层
                to.Ceng = int.Parse(toArray[2]);
                #endregion

                #region write
                var data = new Int16[8];

                data[0] = (Int16)from.Pai;
                data[1] = (Int16)from.Lie;
                data[2] = (Int16)from.Ceng;


                data[3] = (Int16)to.Pai;
                data[4] = (Int16)to.Lie;
                data[5] = (Int16)to.Ceng;

                data[6] = (Int16)asrsjob.Id;

                if (stockStation.Id == 13 || stockStation.Id == 14)
                {
                    //出的类型我们给
                    transLineUtil.Set(stockStation.DirectionType, asrsjob.TYPE == "In" ? 1 : 2);
                    //入我们取一下
                    int Type = transLineUtil.Get(stockStation.DirectionType);
                    if (Type != 1)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [当前有出库任务，请等出库完成后入库!]" });
                        return;
                    }

                    //加个判断,光电匹配下大小板
                    int PlateType = transLineUtil.Get(stockStation.PlateType);
                    if (PlateType != asrsjob.CTNTYPE)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [入料与记录大小板不匹配!]" });
                        return;
                    }
                }

                //写入PLC
                var insertStatus = stackerUtil.Set(StockAddr.FromPai, 7, data);
                if (insertStatus == 0)
                {
                    //单据号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [单据号:{asrsjob.BINDID} 入库库位:{asrsjob.ORIPORT}]" });
                    //托盘号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [托盘号:{asrsjob.CTNCODE}]" });
                    //成功
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令写入成功]" });
                    //库位
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [FROM:{from.Pai}-{from.Lie}-{from.Ceng} TO:{to.Pai}-{to.Lie}-{to.Ceng}]" });
                    //更新
                    var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.One);
                    if (updateResult.Success)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [更新指令状态成功.]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新状态失败:{updateResult.Message}]" });
                }
                #endregion
            }
            catch (Exception ex)
            {
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [异常:{ex.Message}]" });
            }
        }
        #endregion

        #region 出库
        /// <summary>
        /// 出库
        /// </summary>
        private void Out()
        {
            try
            {
                #region get
                //托盘编码
                var stationIds = new List<int>();
                //获取有载流水号
                if (outStockStations != null && outStockStations.Any())
                {
                    outStockStations.ForEach(item =>
                    {
                        //站台有无载
                        var exits = this.transLineUtil.Get(item.HaveTrayAddr);
                        //是否有载
                        if (exits == (int)WorkStatus.Zero)
                            stationIds.Add(item.Id);
                        else
                        {
                            var serialId = this.transLineUtil.Get(item.SerialNoAddr);
                            if (serialId > 0)
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [站台({item.Name})有载]" });
                        }
                    });
                }
                if (!stationIds.Any())
                    stationIds.Add(0);
                //查询条件
                var query = new ASRSJOBQuery()
                {
                    Type = new List<string>()
                    {
                        "Out"
                    },
                    Where = string.Format("TYPE='Out' And (TARPORT LIKE '2-10-01-02' or TARPORT LIKE '2-10-01-04' or TARPORT LIKE '2-10-01-06' or TARPORT LIKE '2-10-01-08' or TARPORT LIKE '2-10-01-10' or TARPORT LIKE '2-10-01-12' or TARPORT LIKE '2-10-01-14')"),
                    State = (int)ASRSJOBStatus.待处理
                };
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetSingleASC(query);
                //指令对象是否为空
                if (asrsjob == null)
                    return;
                if (asrsjob.STATE == (int)WorkStatus.One)
                    return;
                #endregion

                #region from
                //1-10-01-020305
                if (string.IsNullOrEmpty(asrsjob.ORIPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [FROM为空]" }); return;
                }
                //分割
                var tarport = asrsjob.ORIPORT.Split('-');
                //验证长度
                if (tarport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令错误:{asrsjob.TARPORT}]" }); return;
                }
                //分割
                var fromArray = System.Text.RegularExpressions.Regex.Split(tarport[3], @"(?<=\G.{2})(?!$)");

                var from = new From();
                //排
                from.Pai = int.Parse(fromArray[0]);
                //列
                from.Lie = int.Parse(fromArray[1]);
                //层
                from.Ceng = int.Parse(fromArray[2]);
                #endregion

                #region to
                if (string.IsNullOrEmpty(asrsjob.TARPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令错误(from):{asrsjob.TARPORT}为空]" }); return;
                }

                var oriport = asrsjob.TARPORT.Split('-');
                //验证长度
                if (oriport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令错误(from):{asrsjob.TARPORT}长度验证失败]" }); return;
                }
                //入库站台
                var toStationId = int.Parse(oriport[3]);
                //入库站台对象
                var stockStation = stockStations.FirstOrDefault(c => c.Id == toStationId);
                //站台验证
                if (stockStation == null)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [站台({toStationId})查找失败]" }); return;
                }
                //入库状态
                var fromHaveTrayStatus = transLineUtil.Get(stockStation.HaveTrayAddr);
                //验证是否被占用
                if (fromHaveTrayStatus == (int)Success.One)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [站台({toStationId})有载]" }); return;
                }
                var to = new To();
                //排
                to.Pai = stockStation.Pai;
                //列
                to.Lie = stockStation.Lie;
                //层
                to.Ceng = stockStation.Ceng;
                #endregion

                #region write 
                var data = new Int16[8];

                data[0] = (Int16)from.Pai;
                data[1] = (Int16)from.Lie;
                data[2] = (Int16)from.Ceng;

                data[3] = (Int16)to.Pai;
                data[4] = (Int16)to.Lie;
                data[5] = (Int16)to.Ceng;

                data[6] = (Int16)asrsjob.Id;


                if (stockStation.Id == 13 || stockStation.Id == 14)
                {
                    //出的类型我们给
                    transLineUtil.Set(stockStation.DirectionType, asrsjob.TYPE == "In" ? 1 : 2);
                }

                //写入PLC
                var insertStatus = stackerUtil.Set(StockAddr.FromPai, 7, data);
                if (insertStatus == 0)
                {
                    //单据号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [单据号:{asrsjob.BINDID} 入库库位:{asrsjob.ORIPORT}]" });
                    //托盘号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [托盘号:{asrsjob.CTNCODE}]" });
                    //成功
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令写入成功]" });
                    //库位
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [FROM:{from.Pai}-{from.Lie}-{from.Ceng} TO:{to.Pai}-{to.Lie}-{to.Ceng}]" });
                    //更新
                    var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.One);
                    if (updateResult.Success)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新指令状态成功]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新状态失败:{updateResult.Message}]" });
                }
                else
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [批量写入失败]" });
                #endregion
            }
            catch (Exception ex)
            {
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库异常] [{ex.Message}]" });
            }
        }
        #endregion

        #endregion

        #region 自动完成

        #region 入库完成
        /// <summary>
        /// 入库完成
        /// </summary>
        public void InDone()
        {
            try
            {
                //入库完成[D103]
                var inDone = stackerUtil.Get(StockAddr.InDone);
                //验证是否入库完成
                if (inDone != (int)Success.Two)
                    return;
                //流水号
                var serialId = stackerUtil.Get(StockAddr.SerialNo);
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [流水号:{serialId}]" });
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [堆垛机地址:{StockAddr.InDone} 值:{inDone}]" });

                #region get
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetById(serialId);
                //指令对象是否为空
                if (asrsjob == null)
                {
                    //清除入库完成地址
                    var doneStatus = stackerUtil.Set(StockAddr.InDone, (Int16)0);
                    if (doneStatus == 0)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库库完成] [无指令、清除出库信号成功]" });
                    return;
                }
                #endregion

                #region write
                var resultSerialNo = stackerUtil.Set(StockAddr.SerialNo, 0);  //清空流水号
                if (resultSerialNo != 0)
                {
                    throw new Exception($"[入库完成] [清空流水号:{StockAddr.SerialNo}失败.]");
                }
                var resultInDone = stackerUtil.Set(StockAddr.InDone, 0);      //入库完成状态
                if (resultInDone != 0)
                    throw new Exception($"[入库完成] [清空入库完成状态:{StockAddr.InDone}失败.]");

                var resultOutDone = stackerUtil.Set(StockAddr.OutDone, 0);    //出库完成状态
                if (resultOutDone != 0)
                    throw new Exception($"[入库完成] [清空出库完成状态:{StockAddr.OutDone}失败.]");

                var data = new Int16[7]
                {
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0
                };
                //写入PLC
                var result = stackerUtil.Set(StockAddr.FromPai, 7, data);
                if (result == 0)
                {
                    //执行日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [写入指令成功 任务号:{asrsjob.Id}]" });
                    //更新指令
                    var updateResult = ASRSJOBApp.Instance.DeliveryCompleted(asrsjob.Id, (int)CompleteMode.自动);
                    if (updateResult.Success)
                    {
                        //执行日志
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [更新状态成功]" });
                        //界面日志
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [操作流水号:{asrsjob.Id},清空流水号状态:{resultSerialNo},入库完成状态:{resultInDone},出库完成状态:{resultOutDone},清空堆垛机状态:{result}]" });
                    }
                    else
                        throw new Exception("[入库完成] [更新指令状态失败]");
                }
                else
                {
                    //界面日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [批量写入失败]" });
                }
                #endregion

                #region 清除成信息
                {
                    //清除出库完成地址
                    var doneStatus = stackerUtil.Set(StockAddr.InDone, (Int16)0);
                    if (doneStatus == 0)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [清除入库完成信号成功]" });
                }
                #endregion
            }
            catch (Exception ex)
            {
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成异常] [{ex.Message}]" });
            }
        }
        #endregion

        #region 出库完成
        /// <summary>
        /// 出库完成
        /// </summary>
        public void OutDone()
        {
            try
            {
                //出库完成[D104]
                var outDone = stackerUtil.Get(StockAddr.OutDone);
                //验证是否出库完成
                if (outDone != (int)Success.Two)
                    return;
                //获取序列号
                var serialId = stackerUtil.Get(StockAddr.SerialNo);

                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [流水号:{serialId}]" });
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [完成地址{StockAddr.OutDone} 值：{outDone}]" });

                #region get
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetById(serialId);
                //指令对象是否为空
                if (asrsjob == null)
                {
                    //清除出库完成地址
                    var doneStatus = stackerUtil.Set(StockAddr.OutDone, (Int16)0);
                    if (doneStatus == 0)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [无指令、清除出库信号成功]" });
                    return;
                }
                if (string.IsNullOrEmpty(asrsjob.TARPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误(from):{asrsjob.ORIPORT}为空]" }); return;
                }
                var oriport = asrsjob.TARPORT.Split('-');
                //验证长度
                if (oriport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误(from):{asrsjob.TARPORT}长度验证失败]" }); return;
                }
                //当前站台
                var outStockStation = outStockStations.FirstOrDefault(c => c.Id == int.Parse(oriport[3]));
                #endregion

                #region clear
                var data = new Int16[7]
                {
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0
                };
                int result = stackerUtil.Set(StockAddr.FromPai, 7, data);
                if (result == 0)
                {
                    //更新指令
                    var updateResult = ASRSJOBApp.Instance.DeliveryCompleted(asrsjob.Id, (int)CompleteMode.自动);
                    if (updateResult.Success)
                        //界面日志
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [操作流水号:{asrsjob.Id},站台:{outStockStation.Id}]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [更新指令状态失败:{updateResult.Message}]" });
                }
                else
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成][批量写入失败]" });
                #endregion

                #region 清除完成信号
                {
                    //清除出库完成地址
                    var doneStatus = stackerUtil.Set(StockAddr.OutDone, (Int16)0);
                    if (doneStatus == 0)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [清除出库信号成功]" });
                }
                #endregion
            }
            catch (Exception ex)
            {
                //执行日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [异常:{ex.Message}]" });
            }
        }
        #endregion

        #endregion

        #region 手动完成
        /// <summary>
        /// 完成
        /// </summary>
        /// <returns></returns>
        public JResult Complete(int id)
        {
            var response = new JResult()
            {
                Success = false
            };

            #region get
            //获取指令
            var asrsjob = ASRSJOBApp.Instance.GetById(id);
            //对象是否为空
            if (asrsjob == null)
                throw new Exception($"查询暂无记录.");
            #endregion

            #region write
            var data = new Int16[8]
                {
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0
                };
            //写入PLC
            var result = stackerUtil.Set(StockAddr.FromPai, 8, data);
            if (result == 0)
            {
                //更新指令
                var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.Two);
                if (updateResult.Success)
                {
                    response.Success = true;
                    response.Message = "操作状态成功.";
                }
                else
                    throw new Exception("更新状态失败]");
            }
            else
                throw new Exception("清除内容失败.");
            #endregion

            return response;
        }
        #endregion

        #region 释放资源
        /// <summary>
        /// 释放释放资源
        /// </summary>
        public void Dispose()
        {
            //堆垛机线程
            if (stackerThread != null)
                stackerThread.Abort();
            stackerThread = null;

            if (transLineUtil != null)
                transLineUtil.closeWatch();
            transLineUtil = null;

            if (stackerUtil != null)
                stackerUtil.closeWatch();
            stackerUtil = null;
        }
        #endregion

        #region 异常复位
        /// <summary>
        /// 复原
        /// </summary>
        /// <returns></returns>
        public JResult Restore()
        {
            var response = new JResult()
            {
                Success = false
            };

            try
            {
                if (stackerUtil == null)
                {
                    throw new Exception("垛机对象不存在.");
                }

                Task.Run(() =>
                {
                    //脉冲开始
                    stackerUtil.Set(StockAddr.Restore, 1);
                    Task.Delay(3000).Wait();
                    stackerUtil.Set(StockAddr.Restore, 0);
                    //界面日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[异常复原] [完成]" });
                });
            }
            catch (Exception ex)
            {
                response.Message = ex.Message;
            }

            return response;
        }
        #endregion

        #region 设置站台
        /// <summary>
        /// 设置站台
        /// </summary>
        /// <returns></returns>
        public void SetStation(int stationId, int containerCode)
        {
            var response = new JResult()
            {
                Success = false
            };

            try
            {
                if (transLineUtil != null)
                {
                    //入库站台对象
                    var stockStation = stockStations.FirstOrDefault(c => c.Id == stationId);
                    if (stockStation == null)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"站台({stationId})查找失败." }); return;
                    }
                    //托盘号
                    var status = transLineUtil.Set(stockStation.SerialNoAddr, containerCode);
                    if (status == (int)WorkStatus.Zero)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"设置流水号成功 {stockStation.SerialNoAddr}->{containerCode}" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"写入输送线失败" });
                }
                else
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[输送线对象为空]" });
            }
            catch (Exception ex)
            {
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"{ex.Message}" });
            }
        }
        #endregion

        #region 设备异常
        /// <summary>
        /// 设备异常
        /// </summary>
        /// <param name="deviceExceptionCode"></param>
        /// <param name="deviceAddr"></param>
        /// <returns></returns>
        public void GetDeviceException(List<DeviceExceptionCodeResponse> deviceExceptionCode, string addr, int addrValue)
        {
            for (int i = 0; i < 16; i++)
            {
                //索引移位值
                var shiftValue = 1 << i;
                //索引移位置比较?
                if (shiftValue > addrValue)
                {
                    break;
                }

                if (((shiftValue & addrValue) == shiftValue) && (shiftValue <= addrValue))
                {
                    //异常健是否存在
                    var model = deviceExceptionCode.FirstOrDefault(c => c.DeviceAddr == addr && c.Indexs == i);
                    if (model != null)
                    {
                        //界面日志
                        infoEvent(90, new Dictionary<string, string> { ["0"] = $"[{model.Message}]" });
                    }
                }
            }
        }
        #endregion

        #region 清空
        /// <summary>
        /// 清空
        /// </summary>
        /// <returns></returns>
        public JResult Clear()
        {
            var response = new JResult()
            {
                Success = false
            };

            try
            {
                if (stackerUtil == null)
                {
                    throw new Exception("垛机对象不存在.");
                }

                var data = new Int16[8]
                {
                                0,
                                0,
                                0,
                                0,
                                0,
                                0,
                                0,
                                0
                };

                var status = stackerUtil.Set(StockAddr.FromPai, 8, data);
                if (status == 0)
                {
                    response.Success = true;
                    response.Message = "清空垛机地址成功.";
                }
                else
                {
                    response.Success = false;
                    response.Message = "清空垛机地址失败.";
                }
            }
            catch (Exception ex)
            {
                response.Message = ex.Message;
            }

            return response;
        }
        #endregion
    }
}
